#pragma once
#include "Line3d.h"
#include <exception>

namespace Geometry
  {
  Line3d::Line3d(const Point3d& i_point, const Vector3d& i_direction)
    : m_point(i_point)
    {
    if (i_direction.GetLength() == 0.0)
      throw std::exception("Line3d ctor: zero vector as direction");
    m_direction = Vector3d(i_direction[0] / i_direction.GetLength(), i_direction[1] / i_direction.GetLength(), i_direction[2] / i_direction.GetLength());
    }

  Line3d::Line3d(const Point3d& i_point_1, const Point3d& i_point_2)
    {
    if (i_point_1 == i_point_2)
      throw std::exception("Line3d ctor: zero vector as direction");

    m_point = i_point_1;
    double v_x = i_point_1[0] - i_point_2[0];
    double v_y = i_point_1[1] - i_point_2[1];
    double v_z = i_point_1[2] - i_point_2[2];

    m_direction = Vector3d(v_x, v_y, v_z);
    m_direction *= (1 / m_direction.GetLength());
    }

  Point3d Line3d::GetPoint() const
    {
    return m_point;
    }

  Vector3d Line3d::GetDirection() const
    {
    return m_direction;
    }

  bool Line3d::ContainsPoint(const Point3d& i_point) const
    {
    double ratio1 = (i_point[0] - m_point[0]) / m_direction[0];
    double ratio2 = (i_point[1] - m_point[1]) / m_direction[1];
    double ratio3 = (i_point[2] - m_point[2]) / m_direction[2];

    return (ratio1 == ratio2 && ratio2 == ratio3);
    }

  Point3d Line3d::GetProjection(const Point3d& i_point) const
    {
    Point3d pta = m_point;
    Point3d ptb = m_point + m_direction;
    Point3d pts = i_point;

    double numerator =
      (pts[0] - pta[0])*(ptb[0] - pta[0]) +
      (pts[1] - pta[1])*(ptb[1] - pta[1]) +
      (pts[2] - pta[2])*(ptb[2] - pta[2]);

    double denominator =
      (ptb[0] - pta[0])*(ptb[0] - pta[0]) +
      (ptb[1] - pta[1])*(ptb[1] - pta[1]) +
      (ptb[2] - pta[2])*(ptb[2] - pta[2]);

    double x_res = pta[0] + (ptb[0] - pta[0])*numerator / denominator;
    double y_res = pta[1] + (ptb[1] - pta[1])*numerator / denominator;
    double z_res = pta[2] + (ptb[2] - pta[2])*numerator / denominator;

    return Point3d(x_res, y_res, z_res);
    }

  bool operator==(const Line3d& i_left, const Line3d& i_right)
    {
    if (i_left.ContainsPoint(i_right.GetPoint()))
      {
      if (i_left.ContainsPoint(i_right.GetPoint() + i_right.GetDirection()))
        {
        return true;
        }
      }
    return false;
    }

  }
